﻿using System;
using System.Threading;
using Jiubang.Collections;

namespace Jiubang.Communication.Server.Buffers {
    internal sealed class BufferPool {
        private readonly LockFreeStack<byte[]> _stack;
        private int _count;
        private int _hit;
        private int _missed;
        private int _removed;

        public BufferPool(int length) {
            Length = length;

            _stack = new LockFreeStack<byte[]>();
        }

        public int Length { get; private set; }

        public int Starvation {
            get {
                return Count == 0
                           ? int.MaxValue
                           : _missed;
            }
        }

        public int Count {
            get { return _count; }
        }

        public byte[] Create() {
            try {
                return new byte[Length];
            } finally {
                Interlocked.Increment(ref _missed);
            }
        }

        public bool Remove() {
            byte[] value;
            if (_stack.TryPop(out value)) {
                Interlocked.Increment(ref _removed);
                return true;
            }
            return false;
        }

        public bool TryPop(out byte[] value) {
            if (_stack.TryPop(out value)) {
                Interlocked.Decrement(ref _count);
                Interlocked.Increment(ref _hit);
                return true;
            }

            Interlocked.Increment(ref _missed);
            value = new byte[Length];
            return false;
        }

        public void Push(byte[] value) {
            if (value == null)
                throw new ArgumentNullException("value");

            _stack.Push(value);
            Interlocked.Increment(ref _count);
        }
    }
}